﻿using Force.Crc32;
using System.IO.MemoryMappedFiles;

namespace DHTools
{
    public class SharedMemoryWriter : IDisposable
    {
        private const string MapName = "SharedMemoryCustom";
        private const string SemaphoreName = "SharedMemorySemaphore";
        private const string MutexName = "SharedMemoryMutex";
        private const int MemorySize = 1024 * 1024 * 10; // 10MB

        private readonly MemoryMappedFile _mmf;
        private readonly Semaphore _semaphore;
        private readonly Mutex _mutex;

        public SharedMemoryWriter()
        {
            _mmf = MemoryMappedFile.CreateOrOpen(MapName, MemorySize);
            _semaphore = new Semaphore(0, 1, SemaphoreName);
            _mutex = new Mutex(false, MutexName);
        }

        /// <summary>
        /// Writes data to shared memory.
        /// </summary>
        public void WriteData(byte flag, DataType dataType, byte[] data)
        {
            _mutex.WaitOne();

            try
            {
                ulong time = (ulong)DateTimeOffset.Now.ToUnixTimeSeconds();
                ulong dataLength = (ulong)data.Length;
                uint chunkIndex = 0;
                byte compressFlag = flag;
                byte[] reserved = new byte[3];
                uint checksum = Crc32Algorithm.Compute(data);

                using (var accessor = _mmf.CreateViewAccessor())
                {
                    int offset = 0;

                    accessor.Write(offset, time); offset += 8;
                    accessor.Write(offset, (int)dataType); offset += 4;
                    accessor.Write(offset, dataLength); offset += 8;
                    accessor.Write(offset, chunkIndex); offset += 4;
                    accessor.Write(offset, compressFlag); offset += 1;
                    accessor.WriteArray(offset, reserved, 0, 3); offset += 3;

                    accessor.WriteArray(offset, data, 0, data.Length); offset += data.Length;
                    accessor.Write(offset, checksum);
                }

                _semaphore.Release();
            }
            finally
            {
                _mutex.ReleaseMutex();
            }
        }

        /// <summary>
        /// Checks if the data should be compressed based on its length.
        /// </summary>
        public byte ShouldCompress(ulong length)
        {
            return length > MemorySize ? (byte)1 : (byte)0;
        }

        public void Dispose()
        {
            _mmf?.Dispose();
            _semaphore?.Dispose();
            _mutex?.Dispose();
        }
    }
}